package com.gmadmin.configuration.secutity;

import com.gmadmin.common.HttpStatus;
import com.gmadmin.exception.SecurityException;
import com.gmadmin.service.SysApiService;
import com.gmadmin.service.SysRoleService;
import com.gmadmin.entity.vo.api.SysApiVo;
import com.gmadmin.entity.vo.role.SysRoleVo;
import com.gmadmin.entity.vo.user.UserPrincipal;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 认证服务,动态路由认证,验证请求是否存在以及权限是否匹配
 *
 * @author zhangliang
 * @version 1.0
 * @date 2021/1/25 15:36
 */
@Component
public class RBACAuthorityService {

    private final SysRoleService roleService;
    private final RequestMappingHandlerMapping mapping;
    private final SysApiService sysApiService;

    protected static final Logger logger = LoggerFactory.getLogger(RBACAuthorityService.class);

    public RBACAuthorityService(SysRoleService roleService, RequestMappingHandlerMapping mapping, SysApiService sysApiService) {
        this.roleService = roleService;
        this.mapping = mapping;
        this.sysApiService = sysApiService;
    }

    /**
     * 发送请求验证是否有权限
     */
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        //通过RequestMappingHandlerMapping校验请求是否存在
        logger.debug("检查请求是否存在:{}", checkRequest(request));

        Object userInfo = authentication.getPrincipal();
        boolean hasPermission = false;

        if (userInfo instanceof UserDetails) {
            UserPrincipal principal = (UserPrincipal) userInfo;
            Long userId = principal.getId();

            //根据用户id查询角色，再根据角色查询权限菜单
            List<SysRoleVo> roles = roleService.selectByUserId(userId);
            Set<Long> roleIds = roles.stream().map(SysRoleVo::getId).collect(Collectors.toSet());

            //获取api，验证角色是否存在访问的权限
            List<SysApiVo> apiVos = sysApiService.findListByRoleIds(roleIds);

            for (SysApiVo s : apiVos) {
                AntPathRequestMatcher matcher = new AntPathRequestMatcher(s.getPath(), s.getMethod());
                //匹配请求是否有权限
                if (matcher.matches(request)) {
                    hasPermission = true;
                    break;
                }
                if (request.getRequestURI().matches(s.getPath()) && request.getMethod().equals(s.getMethod())) {
                    hasPermission = true;
                    break;
                }

            }
            return hasPermission;
        }

        return false;
    }

    /**
     * 校验请求是否存在
     *
     * @param request 请求
     */
    private boolean checkRequest(HttpServletRequest request) {
        String currentMethod = request.getMethod();
        logger.info(request.getServletPath());

        Multimap<String, String> urlMapping = allUrlMapping();

        for (String uri : urlMapping.keySet()) {
            // 通过 AntPathRequestMatcher 匹配 url
            // 可以通过 2 种方式创建 AntPathRequestMatcher
            // 1：new AntPathRequestMatcher(uri,method) 这种方式可以直接判断方法是否匹配，因为这里我们把 方法不匹配 自定义抛出，所以，我们使用第2种方式创建
            // 2：new AntPathRequestMatcher(uri) 这种方式不校验请求方法，只校验请求路径
            AntPathRequestMatcher antPathMatcher = new AntPathRequestMatcher(uri);
            if (antPathMatcher.matches(request)) {
                if (!urlMapping.get(uri).contains(currentMethod)) {
                    throw new SecurityException(HttpStatus.HTTP_NOT_METHOD);
                } else {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取 所有URL Mapping，返回格式为{"/test":["GET","POST"],"/sys":["GET","DELETE"]}
     *
     * @return {@link ArrayListMultimap} 格式的 URL Mapping
     */
    private Multimap<String, String> allUrlMapping() {
        Multimap<String, String> urlMapping = ArrayListMultimap.create();

        // 获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = mapping.getHandlerMethods();

        handlerMethodMap.forEach((k, v) -> {
            // 获取当前 key 下的获取所有URL
            assert k.getPatternsCondition() != null;
            Set<String> url = k.getPatternsCondition().getPatterns();
            RequestMethodsRequestCondition method = k.getMethodsCondition();


            url.forEach(s -> urlMapping.putAll(s, method.getMethods().stream().map(Enum::toString).collect(Collectors.toList())));
        });

        return urlMapping;
    }
}
